﻿using Microsoft.VisualBasic;
using pdfer.FileObjects;
using pdfer.PdfObjecs;
using pdfer.PdfObjecs.Fonts;
using pdfer.Syntax;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace pdfer.Renders
{
    internal class RenderEngine
    {
        private PageDictionary _pageDic;
        private Byte[] rowData;
        private Int32 width;
        private Int32 height;
        private List<List<Object>> tokens;
        /// <summary>
        /// q 操作符压栈 Q 操作符弹出压栈
        /// </summary>
        private Stack<GraphicsState> graphicsStateStack;
        private GraphicsStateParamsDictionary graphicsStateParams = new();
        private GraphicsState graphicsState;
        public RenderEngine(PageDictionary pageDic) 
        {
            graphicsStateStack = new Stack<GraphicsState>();
            _pageDic = pageDic;
        }

        /// <summary>
        /// RGBA 目前只支持
        /// </summary>
        /// <param name="saveAsPng"></param>
        /// <returns></returns>
        public Byte[] Render(Boolean saveAsPng = false)
        {
            // 读取对应的流
            InnerRender();
            // 获取命名字典
            if(rowData == null || rowData.Length > 0)
            {
                return [];
            }
            if (saveAsPng)
            {
                using var img = Image.LoadPixelData<Rgba32>(rowData, width, height);
                using var ms = new MemoryStream();
                img.SaveAsPng(ms);
                ms.Flush();
                return ms.ToArray();
            }
            return rowData;
        }

        private void InnerRender()
        {
            // 读取token
            ReadAllTokens();

            // 解析token
            EmitToken();
        }

        private void EmitToken()
        {
            List<Object> @params = [];
#if DEBUG
            var fi = new FileInfo("op.txt");
            using var w = fi.AppendText();
#endif
            foreach (var _tokens in tokens)
            {
                // todo 清除状态？？？
#if DEBUG
                w.WriteLine($"cccccc============================");
#endif
                for (int i = 0; i < _tokens.Count; i++)
                {
                    var token = _tokens[i];
                    if (token is PSTokenV2 opera && opera.Type == TokenType2.Opera)
                    {
                    // 操作符号
                    Execute(@params, opera.Token);
#if DEBUG
                    w.WriteLine($"{opera.Token}\t| {JsonSerializer.Serialize(@params)}");
#endif
                    @params = [];
                }
                else
                {
                    @params.Add(token);
                }
            }
        }
        }

        private GraphicsObjectState objectState = GraphicsObjectState.PageDescLevel;

        private void Execute(List<Object> @params, String opertor)
        {
            // 页面级别的操作
            //  General graphics state
            //  Special graphics state
            //  Color
            //  Text state
            //  Marked-content
            //
            // 外部对象（Xobject）(Do进入 操作完立即返回页面级别操作)
            //  
            // 内联图片对象 (BI 进入 EI 退出)
            //  ID
            // 
            // 路径对象（re,m 操作符进入 路径绘制运算符退出）
            //  Path construction
            //  
            // 文本对象 (BT 进入 ET 退出) 
            //  General graphics state
            //  Color
            //  Text state
            //  Text-showing
            //  Text-positioning
            //  Marked-content 
            //  
            // 着色对象 (sh 进入 操作完立即返回)
            //  

            switch (opertor)
            {
                // General graphics state
                case SOperators.w:
                    Op_w(@params);
                    break;
                case SOperators.J:
                    Op_J(@params);
                    break;
                case SOperators.j:
                    Op_j(@params);
                    break;
                case SOperators.M:
                    Op_M(@params);
                    break;
                case SOperators.d:
                    Op_d(@params);
                    break;
                case SOperators.ri:
                    Op_ri(@params);
                    break;
                case SOperators.i:
                    Op_i(@params);
                    break;
                case SOperators.gs:
                    Op_gs(@params);
                    break;
                // Special graphics state
                case SOperators.q:
                    Op_q();
                    break;
                case SOperators.Q:
                    Op_Q();
                    break;
                case SOperators.cm:
                    Op_cm(@params);
                    break;
                // Path construction
                case SOperators.m:
                    Op_m(@params);
                    break;
                case SOperators.l:
                    Op_l(@params);
                    break;
                case SOperators.c:
                    Op_c(@params);
                    break;
                case SOperators.v:
                    Op_v(@params);
                    break;
                case SOperators.y:
                    Op_y(@params);
                    break;
                case SOperators.h:
                    Op_h(@params);
                    break;
                case SOperators.re:
                    Op_re(@params);
                    break;
                    // Path painting
                case SOperators.S:
                case SOperators.s:
                case SOperators.f:
                case SOperators.F:
                case SOperators.f_:
                case SOperators.B:
                case SOperators.B_:
                case SOperators.b:
                case SOperators.b_:
                case SOperators.n:
                    break;
                    //Clipping paths
                case SOperators.W:
                case SOperators.W_:
                    break;
                    //Text objects
                case SOperators.BT:

                    break;
                case SOperators.ET:
                    break;
                    // Text state
                case SOperators.Tc:

                case SOperators.Tw:
                case SOperators.Tz:
                case SOperators.TL:
                case SOperators.Tf:
                case SOperators.Tr:
                case SOperators.Ts:
                    break;
                    // Text positioning
                case SOperators.Td:
                case SOperators.TD:
                case SOperators.Tm:
                case SOperators.T_:
                    break;
                    // Text showing
                case SOperators.TJ:
                case SOperators.Tj:
                case SOperators.__:
                case SOperators.___:
                    break;
                    //Type 3 fonts
                case SOperators.d0:
                case SOperators.d1:
                    break;
                    // Color
                case SOperators.CS:
                case SOperators.cd:
                case SOperators.SC:
                case SOperators.SCN:
                case SOperators.G:
                case SOperators.g:
                case SOperators.RG:
                case SOperators.rg:
                case SOperators.K:
                case SOperators.k:
                    break;
                    // Shading patterns
                case SOperators.sh:
                    break;
                    //  Inline images
                case SOperators.BI:
                case SOperators.ID:
                case SOperators.EI:
                    break;
                    //  XObjects
                case SOperators.Do:
                    break;
                    // Marked content
                case SOperators.MP:
                case SOperators.DP:
                case SOperators.BMC:
                case SOperators.BDC:
                case SOperators.EMC:
                    break;
                    // Compatibility
                case SOperators.BX:
                case SOperators.EX:
                    break;
            }
        }

        /// <summary>
        /// 三次内塞尔曲线
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="p3"></param>
        private void BezierCurves(Double p0, Double p1, Double p2, Double p3)
        {
            // R(t) = (1-t)^3 * p0 + 3t(1-t)^2 * p1 + 3t^2 * (1 - t) * p2 + t^3 *p3
        }

        #region 方法

        private void Op_T_(List<object> @params)
        {
            // TODO
        }

        private void Op_Tm(List<object> @params)
        {
            // TODO
        }

        private void Op_TD(List<object> @params)
        {
            // TODO
        }

        private void Op_Td(List<object> @params)
        {
            // TODO
        }

        private void Op____(List<object> @params)
        {
            // TODO
        }

        private void Op___(List<object> @params)
        {
            // TODO
        }

        private void Op_Tj(List<object> @params)
        {
            // TODO
        }

        private void Op_TJ(List<object> @params)
        {
            // TODO
        }

        private void Op_Ts(List<object> @params)
        {
            graphicsState.TextState.Trise = Double.Parse(@params[0].ToString());
        }

        private void Op_Tr(List<object> @params)
        {
            graphicsState.TextState.Tmode = Int32.Parse(@params[0].ToString());
        }
        private void Op_Tf(List<object> @params)
        {
            graphicsState.TextState.Tf = @params[0].ToString();
            graphicsState.TextState.Tfs = Double.Parse(@params[1].ToString());
        }

        private void Op_TL(List<object> @params)
        {
            graphicsState.TextState.Tl = Double.Parse(@params[0].ToString());
        }

        private void Op_Tz(List<object> @params)
        {
            graphicsState.TextState.Th = Double.Parse(@params[0].ToString());
        }


        private void Op_Tw(List<object> @params)
        {
            graphicsState.TextState.Tw = Double.Parse(@params[0].ToString());
        }

        private void Op_Tc(List<object> @params)
        {
            graphicsState.TextState.Tc = Double.Parse(@params[0].ToString());
        }

        /// <summary>
        /// 将矩形作为完整的子路径附加到当前路径，该子路径具有左下角 （x， y） 以及用户空间中的宽度和高度尺寸。
        /// x y width height re 相当于
        ///     x y m 
        ///     (x + width) y l 
        ///     (x + width) (y + height) l 
        ///     x (y + height) l 
        ///     h
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_re(List<object> @params)
        {
            
        }

        /// <summary>
        /// 通过附加从当前点到子路径起点的直线段来关闭当前子路径。如果当前子路径已关闭，则 h 不执行任何作。
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_h(List<object> @params)
        {
            
        }

        /// <summary>
        /// 将三次 Bezier 曲线附加到当前路径。
        /// <para>
        /// 曲线从当前点延伸到点 （x3 ， y3 ），使用 （x1 ， y1 ） 和 （x3 ， y3 ） 作为贝塞尔控制点（请参阅下面的“三次贝塞尔曲线”）。
        /// </para>
        /// 新的当前点是 （x3 ， y3 ）。
        /// <para>
        /// x1 y1 x3 y3
        /// </para>
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_y(List<object> @params)
        {
            
        }

        /// <summary>
        /// 将三次 Bezier 曲线附加到当前路径。
        /// <para>
        /// 曲线从当前点延伸到点 （x3 ， y3 ），使用当前点和 （x2 ， y2 ） 作为贝塞尔控制点（请参阅下面的“三次贝塞尔曲线”）。
        /// </para>
        /// 新的当前点是 （x3 ， y3 ）。
        /// <para></para>
        ///  x2 y2 x3 y3
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_v(List<object> @params)
        {
            
        }

        /// <summary>
        /// 将三次 Bezier 曲线附加到当前路径。
        /// <para>
        /// 曲线从当前点延伸到点 （x3 ， y3 ），使用 （x1 ， y1 ） 和 （x2 ， y2 ） 作为贝塞尔控制点（请参阅下面的“三次贝塞尔曲线”）。
        /// </para>
        /// 新的当前点是 （x3 ， y3 ）。
        /// <para></para>
        /// x1 y1 x2 y2 x3 y3
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_c(List<object> @params)
        {
            
        }

        /// <summary>
        /// 从当前点到点 （x， y） 追加一条直线段。新的当前点是 （x， y）。
        /// <para></para>
        ///  x y
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_l(List<object> @params)
        {
        }

        /// <summary>
        /// 通过将当前点移动到坐标 （x， y） 来开始新的子路径，省略任何连接的线段。
        /// 如果当前路径中的前一个路径构造运算符也是 m，则新的 m 将覆盖它;路径中不会保留上一个 M作的痕迹。
        ///  <para>
        ///  x y
        ///  </para>
        /// </summary>
        /// <param name="params"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Op_m(List<object> @params)
        {
            

        }

        private void Op_gs(List<Object> @params)
        {
            var p1 = @params[0].ToString();
            // 在 资源子字典ExtGState中找到对应的字典
            // TODO


        }

        private void Op_i(List<Object> @params)
        {
            graphicsState.Flatness = Double.Parse(@params[0].ToString());
        }

        /// <summary>
        /// 在图形状态中设置颜色呈现意图
        /// </summary>
        /// <param name="params"></param>
        private void Op_ri(List<Object> @params)
        {
            graphicsState.RenderingIntent = @params[0].ToString();
        }

        /// <summary>
        /// 在图形状态中设置线条虚线模式
        /// </summary>
        /// <param name="params"></param>
        private void Op_d(List<Object> @params)
        {
            Int32[] p1 = null;
            if (@params[0] is IList arr)
            {
                p1 = new Int32[arr.Count];
                for (int i = 0; i < arr.Count; i++)
                {
                    p1[i] = Int32.Parse(arr[i].ToString());
                }
            }
            else
            {
                p1 = [];
            }
            var p2 = Int32.Parse(@params[1].ToString());
            graphicsState.DashPattern = [p2, .. p1];
        }

        /// <summary>
        /// 在图形状态中设置斜接限制
        /// </summary>
        /// <param name="params"></param>
        private void Op_M(List<Object> @params)
        {
            graphicsState.MiterLimit = Double.Parse(@params[0].ToString());
        }

        /// <summary>
        /// 在图形状态中设置线条连接样式
        /// </summary>
        /// <param name="params"></param>
        private void Op_j(List<Object> @params)
        {
            graphicsState.LineJoin = Int32.Parse(@params[0].ToString());
        }

        /// <summary>
        /// 在图形状态中设置线帽样式
        /// </summary>
        /// <param name="params"></param>
        private void Op_J(List<Object> @params)
        {
            graphicsState.LineCap = Int32.Parse(@params[0].ToString());
        }

        /// <summary>
        /// 在图形状态中设置线宽
        /// </summary>
        /// <param name="params"></param>
        private void Op_w(List<Object> @params)
        {
            graphicsState.LineWidth = Double.Parse(@params[0].ToString());
        }

        private void Op_cm(List<Object> @params)
        {
            var tmp = new Double[6];
            for (var i = 0; i < @params.Count; i++)
            {
                tmp[i] = Double.Parse(@params[i].ToString());
            }
            graphicsState.CTM = tmp;
        }

        private void Op_Q()
        {
            graphicsState = graphicsStateStack.Pop();
        }

        private void Op_q()
        {
            graphicsStateStack.Push(graphicsState);
            graphicsState = new GraphicsState();
        }
        #endregion

        // 读取所有Token
        private void ReadAllTokens()
        {
            var contents = _pageDic.Contents;
            if (contents == null || contents.Count == 0)
            {
                return;
            }
            var _tokens = new List<List<Object>>();
            for (int i = 0; i < contents.Count; i++)
            {
                var content = contents[i];

                var obj = _pageDic.GetIndirectObj(content);

                if (obj == null && obj.StreamData == null)
                {
                    continue;
                }
                var sps = new SPSCompilationEngine();
                sps.ParseToPSToken(obj.DecodeStreamData);
                var __tokens = sps.GetStreamObjs();
                _tokens.Add(__tokens);
            }
            tokens = _tokens;
        }

        #region 导出文本信息

        /// <summary>
        /// 0 render 1 text 2 image
        /// </summary>
        private Int32 state = 0;

        public List<String> GetAllText()
        {
            state = 1;
            // 读取token
            ReadAllTokens();
            ReadAllText();
            state = 0;
            return charInfo;

        }

        private List<String> charInfo;
        private void ReadAllText()
        {
            charInfo = [];

            foreach (var _tokens in tokens)
            {
                for (int i = 0; i < _tokens.Count; i++)
            {
                    var token = _tokens[i];
                if (token is PSTokenV2 opera 
                    && opera.Type == TokenType2.Opera
                    && opera.Token == "BT"
                    )
                {
                        ReadTxtObject(_tokens, ref i);
                    }
                }
            }

        }

        /// <summary>
        /// 只读取文本信息 不需要位置信息
        /// 字体信息 + Tj | TJ 操作符号
        /// </summary>
        /// <param name="i"></param>
        private void ReadTxtObject(List<Object> tokens, ref int i)
        {
            Op_q();
            i++;
            List<Object> @params = [];
            for (; i < tokens.Count; i++)
            {
                var token = tokens[i];
                if (token is PSTokenV2 opera && opera.Type == TokenType2.Opera)
                {
                    if(opera.Token == "ET")
                    {
                        Op_Q();
                        break;
                    }
                    ExecuteText(@params, opera.Token);
                    @params = [];
                }
                else
                {
                    @params.Add(token);
                }


            }
        }

        private void ExecuteText(List<Object> @params, String opera)
        {
            // 字体相关的和输出相关的 位置相关的暂时不管

            switch (opera)
            {
                case SOperators.Tc:
                    Op_Tc(@params);
                    break;
                case SOperators.Tw:
                    Op_Tw(@params);
                    break;
                case SOperators.Tz:
                    Op_Tz(@params);
                    break;
                case SOperators.TL:
                    Op_TL(@params);
                    break;
                case SOperators.Tf:
                    Op_Tf(@params);
                    break;
                case SOperators.Tr:
                    Op_Tr(@params);
                    break;
                case SOperators.Ts:
                    Op_Ts(@params);
                    break;
                case SOperators.Td:
                    Op_Td(@params);
                    break;
                case SOperators.TD:
                    Op_TD(@params);
                    break;
                case SOperators.Tm:
                    Op_Tm(@params);
                    break;
                case SOperators.T_:
                    Op_T_(@params);
                    break;
                case SOperators.TJ:
                    // 根据参数和目前的文本状态上下文输出文本
                    if (@params[0] is List<Object> p1)
                    {
                        charInfo.Add( GenericUnicode(p1) );
                    }
                    // Op_TJ(@params);
                    break;
                case SOperators.Tj:
                    // 根据参数和目前的文本状态上下文输出文本
                    // 参数是文本 
                    // 按字节查找对应的字符
                    charInfo.Add( GenericUnicode(@params[0]) );
                    // Op_Tj(@params);
                    break;
                case SOperators.__:
                    // 根据参数和目前的文本状态上下文输出文本
                    // Op___(@params);
                    break;
                case SOperators.___:
                    // 根据参数和目前的文本状态上下文输出文本
                    // Op____(@params);
                    break;
            }




        }
        private String GenericUnicode(List<Object> @params)
        {
            var sb = new StringBuilder();
            for(var i = 0; i< @params.Count; i++)
            {
                var p1 = @params[i];
                if(i + 1 >= @params.Count)
                {
                    sb.Append(GenericUnicode(p1));
                }
                else
                {
                    var p2 = @params[i+1];
                    if (p2 is Int32 _p2)
                    {
                        i++;
                        sb.Append(GenericUnicode(p1, _p2));
                    }
                    else
                    {
                        sb.Append(GenericUnicode(p1));
                    }
                }
            }
            return sb.ToString();
        }
        private String GenericUnicode(Object obj, Int32 width = 0)
        {
            if(obj is PSTokenV2 token)
            {
                var bytespan = token.ByteToken[1..^1].AsSpan();
                // 获取当前字体
                var tf = graphicsState.TextState.Tf;
                if(_pageDic.Resources[PdfName.Key_Font] is Dictionary<String, Object> fontDic) 
                {
                    if(fontDic.TryGetValue(tf, out var r))
                    {
                        var fileObj = _pageDic.GetIndirectObj(r.ToString());
                        if(fileObj.TryGetValue(PdfName.Key_Subtype, out var _fontType) && _fontType is String fontType)
                        {
                            switch (fontType)
                            {
                                case PdfTypeConst.Type_Font_Type0:
                                    {
                                        var fontObj = _pageDic.GetIndirectObj<FontType0Dictionary>(r.ToString());
                                        return fontObj.GetString(bytespan);
                                    }
                                case PdfTypeConst.Type_Font_Type1:
                                case PdfTypeConst.Type_Font_MMType1:
                                    {
                                        var fontObj = _pageDic.GetIndirectObj<FontType1Dictionary>(r.ToString());
                                        if (fontObj.ToUnicode == null)
                                        {
                                            if(fontObj.Encoding == "/WinAnsiEncoding")
                                            {
                                                return Encoding.ASCII.GetString(bytespan);
                                            }
                                            else
                                            {
                                                return Encoding.ASCII.GetString(bytespan);
                                            }
                                        }
                                        else
                                        {
                                            var toU = fontObj.ToUnicode;
                                            var cmapEngine = new CMapCompilationEngine();
                                            cmapEngine.ParseToCMapObject(toU.DecodeStreamData);
                                            var cmap = cmapEngine.CMapObj;

                                            return cmap.GetString(bytespan);
                                        }

                                    }
                                case PdfTypeConst.Type_Font_Type3:
                                    {
                                        var fontObj = _pageDic.GetIndirectObj<FontType3Dictionary>(r.ToString());
                                        var toU = fontObj.ToUnicode;
                                        var cmapEngine = new CMapCompilationEngine();
                                        cmapEngine.ParseToCMapObject(toU.DecodeStreamData);
                                        var cmap = cmapEngine.CMapObj;

                                        return cmap.GetString(bytespan);
                                    }
                                case PdfTypeConst.Type_Font_TrueType:
                                    break;
                                case PdfTypeConst.Type_Font_CIDFont0:
                                case PdfTypeConst.Type_Font_CIDFont2:

                                    break;
                            }
                        }
                    }
                }
                // 字体类型

            }
            return String.Empty;
        }

        #endregion
    }

    internal enum GraphicsObjectState
    {
        PageDescLevel,
        PathObject,
        ClippingPathObject,
        InlineImageObject,
        TextObject,
        ShadingObject,
        ExternalObject
    }

    internal static class SOperators
    {
        //w, J, j, M, d, ri, i, gs,
        public const String w = "w";
        public const String J = "J";
        public const String j = "j";
        public const String M = "M";
        public const String d = "d";
        public const String ri = "ri";
        public const String i = "i";
        public const String gs = "gs";
        //q, Q, cm,
        public const String q = "q";
        public const String Q = "Q";
        public const String cm = "cm";
        //m, l, c, v, y, h, re,
        public const String m = "m";
        public const String l = "l";
        public const String c = "c";
        public const String v = "v";
        public const String y = "y";
        public const String h = "h";
        public const String re = "re";
        //S, s, f, F, f_, B, B_, b, b_, n,
        public const String S = "S";
        public const String s = "s";
        public const String f = "f";
        public const String F = "F";
        public const String f_ = "f*";
        public const String B = "B";
        public const String B_ = "B*";
        public const String b = "b";
        public const String b_ = "b*";
        public const String n = "n";
        //W, W_,
        public const String W = "W";
        public const String W_ = "W*";
        //BT, ET,
        public const String BT = "BT";
        public const String ET = "ET";
        //Tc, Tw, Tz, TL, Tf, Tr, Ts,
        public const String Tc = "Tc";
        public const String Tw = "Tw";
        public const String Tz = "Tz";
        public const String TL = "TL";
        public const String Tf = "Tf";
        public const String Tr = "Tr";
        public const String Ts = "Ts";
        //Td, TD, Tm, T_,
        public const String Td = "Td";
        public const String TD = "TD";
        public const String Tm = "Tm";
        public const String T_ = "T*";
        //Tj, TJ, __, ___,
        public const String Tj = "Tj";
        public const String TJ = "TJ";
        public const String __ = "'";
        public const String ___ = "\"";
        //d0, d1,
        public const String d0 = "d0";
        public const String d1 = "d1";
        //CS, cd, SC, SCN, G, g, RG, rg, K, k,
        public const String CS = "CS";
        public const String cd = "cd";
        public const String SC = "SC";
        public const String SCN = "SCN";
        public const String G = "G";
        public const String g = "g";
        public const String RG = "RG";
        public const String rg = "rg";
        public const String K = "K";
        public const String k = "k";
        //sh,
        public const String sh = "sh";
        //BI, ID, EI,
        public const String BI = "BI";
        public const String ID = "ID";
        public const String EI = "EI";
        //Do,
        public const String Do = "Do";
        //MP, DP, BMC, BDC, EMC,
        public const String MP = "MP";
        public const String DP = "DP";
        public const String BMC = "BMC";
        public const String BDC = "BDC";
        public const String EMC = "EMC";
        //BX, EX
        public const String BX = "BX";
        public const String EX = "EX";
    }
}
